﻿namespace Okazuki.MVVM.PrismSupport.FSharp
{
    using System;
    using System.Collections.Generic;
    using System.Windows.Input;
    using Microsoft.FSharp.Quotations;
    using Microsoft.Practices.Prism.Commands;
    using Microsoft.Practices.Prism.ViewModel;
    using System.Diagnostics;

    public abstract class FsViewModelBase : NotificationObject
    {
        private static EventHandler MakeEventHandler(DelegateCommandBase command)
        {
            EventHandler h = (s, e) => command.RaiseCanExecuteChanged();
            return h;
        }

        private List<EventHandler> requerySuggestedEventHandlers = new List<EventHandler>();
        
        protected FsViewModelBase()
        {
        }

        protected void set<T>(ref T field, T value, FSharpExpr<T> expression)
        {
            var r = PatternsModule.PropertyGetPattern(expression);
            try
            {
                var propertyName = r.Value.Item2.Name;
                this.set(ref field, value, propertyName);
            }
            catch (NullReferenceException ex)
            {
                throw new ArgumentException("expressionにはプロパティ構文しか指定できません", ex);
            }
        }

        protected virtual void set<T>(ref T field, T value, string propertyName)
        {
            if (Equals(field, value))
            {
                return;
            }

            field = value;
            this.RaisePropertyChanged(propertyName);
        }

        protected DelegateCommand command(ref DelegateCommand command, Action execute, Func<bool> canExecute)
        {
            if (command != null)
            {
                return command;
            }

            command = new DelegateCommand(execute, canExecute);
            var handler = MakeEventHandler(command);
            this.requerySuggestedEventHandlers.Add(handler);
            CommandManager.RequerySuggested += handler;
            return command;
        }

        protected DelegateCommand command(ref DelegateCommand command, Action execute)
        {
            return this.command(ref command, execute, () => true);
        }

        protected DelegateCommand<T> command<T>(ref DelegateCommand<T> command, Action<T> execute, Func<T, bool> canExecute)
        {
            if (command != null)
            {
                return command;
            }

            command = new DelegateCommand<T>(execute, canExecute);
            var handler = MakeEventHandler(command);
            this.requerySuggestedEventHandlers.Add(handler);
            CommandManager.RequerySuggested += handler;
            return command;
        }

        protected DelegateCommand<T> command<T>(ref DelegateCommand<T> command, Action<T> execute)
        {
            return this.command(ref command, execute, v => true);
        }
    }
}
